package com.piece.module.flowable.util;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.piece.admin.api.dto.SysUserDTO;
import com.piece.admin.service.TbSysUserService;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.collection.CollectionUtil;
import com.piece.core.framework.util.collection.MapUtil;
import com.piece.core.framework.util.object.BeanUtil;
import com.piece.core.framework.util.object.ObjectUtil;
import com.piece.core.framework.util.basic.SpringUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.log.client.dto.UserDTO;
import com.piece.core.log.config.UserContextHolder;
import com.piece.module.flowable.constants.ProcessConstants;
import com.piece.module.flowable.constants.TaskConstants;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.Task;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程用户相关工具类
 */
public class UserTaskUtil {

    /**
     * 获取所有用户任务节点
     *
     * @param model bpmnModel对象
     */
    public static Collection<UserTask> getAllUserTaskEvent(BpmnModel model) {
        Process process = model.getMainProcess();
        Collection<FlowElement> flowElements = process.getFlowElements();
        return getAllUserTaskEvent(flowElements, null);
    }

    /**
     * 获取所有用户任务节点
     *
     * @param flowElements 流程元素集合
     * @param allElements 所有流程元素集合
     */
    public static Collection<UserTask> getAllUserTaskEvent(Collection<FlowElement> flowElements, Collection<UserTask> allElements) {
        allElements = null == allElements ? new ArrayList<>() : allElements;
        for (FlowElement flowElement : flowElements) {
            if (flowElement instanceof UserTask) {
                allElements.add((UserTask) flowElement);
            }
            if (flowElement instanceof SubProcess) {
                // 继续深入子流程，进一步获取子流程
                allElements = getAllUserTaskEvent(((SubProcess) flowElement).getFlowElements(), allElements);
            }
        }
        return allElements;
    }

    /**
     * 获取用户组信息
     */
    public static List<String> getCandidateGroup() {
        List<String> list = new ArrayList<>();
        UserDTO user = UserContextHolder.get();
        if (ObjectUtil.isNotNull(user)) {
            if (ObjectUtil.isNotEmpty(user.getPermission())) {
                Arrays.stream(user.getPermission()).forEach(role -> {
                    list.add(TaskConstants.ROLE_GROUP_PREFIX + role);
                });
            }
            if (ObjectUtil.isNotNull(user.getDeptId())) {
                list.add(TaskConstants.DEPT_GROUP_PREFIX + user.getDeptId());
            }
        }
        return list;
    }

    /**
     * 获取下一步骤的用户任务
     */
    public static List<UserTask> getNextUserTasks(RepositoryService repositoryService, ProcessDefinition processDefinition, Map<String, Object> map) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        Process mainProcess = bpmnModel.getMainProcess();
        Collection<FlowElement> flowElements = mainProcess.getFlowElements();
        FlowElement startFlowElement = getStartFlowElement(mainProcess.getFlowElements());
        return getNextUserTasks(bpmnModel, startFlowElement.getId(), map);
    }

    /**
     * 获取下一步骤的用户任务
     */
    public static List<UserTask> getNextUserTasks(RepositoryService repositoryService, Task task, Map<String, Object> map) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        return getNextUserTasks(bpmnModel, task.getTaskDefinitionKey(), map);
    }

    private static List<UserTask> getNextUserTasks(BpmnModel bpmnModel, String definitionKey, Map<String, Object> map) {
        List<UserTask> data = new ArrayList<>();
        Process mainProcess = bpmnModel.getMainProcess();
        Collection<FlowElement> flowElements = mainProcess.getFlowElements();
        FlowElement flowElement = bpmnModel.getFlowElement(definitionKey);
        userTask(flowElements, flowElement, map, data);
        return data;
    }

    /**
     * 获取当前步骤的用户任务
     */
    public static void userTask(Collection<FlowElement> flowElements, FlowElement flowElement, Map<String, Object> map, List<UserTask> nextUser) {
        //如果是结束节点
        if (flowElement instanceof EndEvent) {
            //如果是子任务的结束节点
            if (getSubProcess(flowElements, flowElement) != null) {
                flowElement = getSubProcess(flowElements, flowElement);
            }
        }
        //获取Task的出线信息--可以拥有多个
        List<SequenceFlow> outGoingFlows = null;
        if (flowElement instanceof org.flowable.bpmn.model.Task) {
            outGoingFlows = ((org.flowable.bpmn.model.Task) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof Gateway) {
            outGoingFlows = ((Gateway) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof StartEvent) {
            outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof SubProcess) {
            outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof CallActivity) {
            outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows();
        }
        if (null != outGoingFlows && outGoingFlows.size() > 0) {
            //遍历所有的出线--找到可以正确执行的那一条
            for (SequenceFlow sequenceFlow : outGoingFlows) {
                //1.有表达式，且为true
                //2.无表达式
                String expression = sequenceFlow.getConditionExpression();
                if (null == expression || expressionResult(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))) {
                    //出线的下一节点
                    String nextFlowElementID = sequenceFlow.getTargetRef();
                    if (checkSubProcess(nextFlowElementID, flowElements, nextUser)) {
                        continue;
                    }

                    //调用流程
                    if (flowElement instanceof CallActivity) {
                        CallActivity ca = (CallActivity) flowElement;
                        if (ca.getLoopCharacteristics() != null) {
                            UserTask userTask = new UserTask();
                            userTask.setId(ca.getId());
                            userTask.setLoopCharacteristics(ca.getLoopCharacteristics());
                            userTask.setName(ca.getName());
                            nextUser.add(userTask);
                        }
                        userTask(flowElements, flowElement, map, nextUser);
                    }

                    //查询下一节点的信息
                    FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements);
                    //用户任务
                    if (flowElement instanceof UserTask) {
                        nextUser.add((UserTask) flowElement);
                    }
                    //排他网关
                    else if (flowElement instanceof ExclusiveGateway) {
                        userTask(flowElements, nextFlowElement, map, nextUser);
                    }
                    //并行网关
                    else if (flowElement instanceof ParallelGateway) {
                        userTask(flowElements, nextFlowElement, map, nextUser);
                    }
                    //接收任务
                    else if (flowElement instanceof ReceiveTask) {
                        userTask(flowElements, nextFlowElement, map, nextUser);
                    }
                    //服务任务
                    else if (flowElement instanceof ServiceTask) {
                        userTask(flowElements, nextFlowElement, map, nextUser);
                    }
                    //子任务的起点
                    else if (flowElement instanceof StartEvent) {
                        userTask(flowElements, nextFlowElement, map, nextUser);
                    }
                    //结束节点
                    else if (flowElement instanceof EndEvent) {
                        userTask(flowElements, nextFlowElement, map, nextUser);
                    }
                }
            }
        }
    }

    /**
     * 判断是否是多实例子流程并且需要设置集合类型变量
     */
    public static boolean checkSubProcess(String Id, Collection<FlowElement> flowElements, List<UserTask> nextUser) {
        for (FlowElement flowElement1 : flowElements) {
            if (flowElement1 instanceof SubProcess && flowElement1.getId().equals(Id)) {
                SubProcess sp = (SubProcess) flowElement1;
                if (null != sp.getLoopCharacteristics()) {
                    String inputDataItem = sp.getLoopCharacteristics().getInputDataItem();
                    UserTask userTask = new UserTask();
                    userTask.setId(sp.getId());
                    userTask.setLoopCharacteristics(sp.getLoopCharacteristics());
                    userTask.setName(sp.getName());
                    nextUser.add(userTask);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 查询一个节点的是否子任务中的节点
     *
     * @param flowElements 全流程的节点集合
     * @param flowElement  当前节点
     */
    public static FlowElement getSubProcess(Collection<FlowElement> flowElements, FlowElement flowElement) {
        for (FlowElement flowElement1 : flowElements) {
            if (flowElement1 instanceof SubProcess) {
                for (FlowElement flowElement2 : ((SubProcess) flowElement1).getFlowElements()) {
                    if (flowElement.equals(flowElement2)) {
                        return flowElement1;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 查询流程节点对象
     *
     * @param Id           节点ID
     * @param flowElements 流程节点集合
     */
    public static FlowElement getFlowElementById(String Id, Collection<FlowElement> flowElements) {
        for (FlowElement flowElement : flowElements) {
            if (flowElement.getId().equals(Id)) {
                //如果是子任务，则查询出子任务的开始节点
                if (flowElement instanceof SubProcess) {
                    return getStartFlowElement(((SubProcess) flowElement).getFlowElements());
                }
                return flowElement;
            }
            if (flowElement instanceof SubProcess) {
                FlowElement flowElement1 = getFlowElementById(Id, ((SubProcess) flowElement).getFlowElements());
                if (null != flowElement1) {
                    return flowElement1;
                }
            }
        }
        return null;
    }

    /**
     * 返回流程的开始节点
     *
     * @param flowElements 节点集合
     */
    public static FlowElement getStartFlowElement(Collection<FlowElement> flowElements) {
        for (FlowElement flowElement : flowElements) {
            if (flowElement instanceof StartEvent) {
                return flowElement;
            }
        }
        return null;
    }

    public static List<SysUserDTO> getFlowCandidate(UserTask userTask, String dataType) {
        TbSysUserService tbSysUserService = SpringUtil.getBean(TbSysUserService.class);
        List<SysUserDTO> userList = new ArrayList<>();
        Long deptId = Convert.toLong(UserContextHolder.getDeptId());

        switch (dataType) {
            case ProcessConstants.USER_TYPE_INITIATOR:
            case ProcessConstants.USER_TYPE_USERS:
                // 指定用户
                if (StringUtil.isNotEmpty(userTask.getAssignee())) {
                    userList = tbSysUserService.findList(MapUtil.builder(new HashMap<String, Object>())
                            .put("userId", Convert.toLong(userTask.getAssignee()))
                            .build()).stream().map(user -> {
                                SysUserDTO sysUser = new SysUserDTO();
                                BeanUtil.copyProperties(user, sysUser, true);
                                return sysUser;
                            }).collect(Collectors.toList());
                } else if (ObjectUtil.isNotEmpty(userTask.getCandidateUsers())) {
                    List<Long> candidateUsers = Convert.toLongList(userTask.getCandidateUsers());
                    userList = tbSysUserService.findList(MapUtil.builder(new HashMap<String, Object>())
                            .put("Q_userId_S_IN", candidateUsers)
                            .build()).stream().map(user -> {
                        SysUserDTO sysUser = new SysUserDTO();
                        BeanUtil.copyProperties(user, sysUser, true);
                        return sysUser;
                    }).collect(Collectors.toList());
                }
                break;
            case ProcessConstants.USER_TYPE_ROLES:
                // 指定本部门角色
                List<Long> deptRoleGroups = Convert.toLongList(userTask.getCandidateGroups());
                userList = tbSysUserService.findUserByDeptRoles(deptId, deptRoleGroups, true)
                        .stream().map(user -> {
                            SysUserDTO sysUser = new SysUserDTO();
                            BeanUtil.copyProperties(user, sysUser, true);
                            return sysUser;
                        }).collect(Collectors.toList());
                break;
            case ProcessConstants.USER_TYPE_POSTS:
                // 指定部门领导
                List<Long> deptPostGroups = Convert.toLongList(userTask.getCandidateGroups());
                if (CollectionUtil.isNotEmpty(deptPostGroups)) {
                    userList = tbSysUserService.findUserByDeptPosts(deptId, deptPostGroups, true)
                            .stream().map(user -> {
                                SysUserDTO sysUser = new SysUserDTO();
                                BeanUtil.copyProperties(user, sysUser, true);
                                return sysUser;
                            }).collect(Collectors.toList());
                }
                break;
        }

        return userList;
    }

    /**
     * 校验el表达式
     */
    public static boolean expressionResult(Map<String, Object> map, String expression) {
        Expression exp = AviatorEvaluator.compile(expression);
        final Object execute = exp.execute(map);
        return Boolean.parseBoolean(String.valueOf(execute));
    }
}
